use crate::abi::AbiVariant;
use anyhow::{Context, Result, bail};
use id_arena::{Arena, Id};
use indexmap::IndexMap;
use semver::Version;
use std::borrow::Cow;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::path::Path;

#[cfg(feature = "decoding")]
pub mod decoding;
#[cfg(feature = "decoding")]
mod metadata;
#[cfg(feature = "decoding")]
pub use metadata::PackageMetadata;

pub mod abi;
mod ast;
pub use ast::SourceMap;
use ast::lex::Span;
pub use ast::{ParsedUsePath, parse_use_path};
mod sizealign;
pub use sizealign::*;
mod resolve;
pub use resolve::*;
mod live;
pub use live::{LiveTypes, TypeIdVisitor};

#[cfg(feature = "serde")]
use serde_derive::Serialize;
#[cfg(feature = "serde")]
mod serde_;
#[cfg(feature = "serde")]
use serde_::*;

/// Checks if the given string is a legal identifier in wit.
pub fn validate_id(s: &str) -> Result<()> {
    ast::validate_id(0, s)?;
    Ok(())
}

pub type WorldId = Id<World>;
pub type InterfaceId = Id<Interface>;
pub type TypeId = Id<TypeDef>;

/// Representation of a parsed WIT package which has not resolved external
/// dependencies yet.
///
/// This representation has performed internal resolution of the WIT package
/// itself, ensuring that all references internally are valid and the WIT was
/// syntactically valid and such.
///
/// The fields of this structure represent a flat list of arrays unioned from
/// all documents within the WIT package. This means, for example, that all
/// types from all documents are located in `self.types`. The fields of each
/// item can help splitting back out into packages/interfaces/etc as necessary.
///
/// Note that an `UnresolvedPackage` cannot be queried in general about
/// information such as size or alignment as that would require resolution of
/// foreign dependencies. Translations such as to-binary additionally are not
/// supported on an `UnresolvedPackage` due to the lack of knowledge about the
/// foreign types. This is intended to be an intermediate state which can be
/// inspected by embedders, if necessary, before quickly transforming to a
/// [`Resolve`] to fully work with a WIT package.
///
/// After an [`UnresolvedPackage`] is parsed it can be fully resolved with
/// [`Resolve::push`]. During this operation a dependency map is specified which
/// will connect the `foreign_deps` field of this structure to packages
/// previously inserted within the [`Resolve`]. Embedders are responsible for
/// performing this resolution themselves.
#[derive(Clone)]
pub struct UnresolvedPackage {
    /// The namespace, name, and version information for this package.
    pub name: PackageName,

    /// All worlds from all documents within this package.
    ///
    /// Each world lists the document that it is from.
    pub worlds: Arena<World>,

    /// All interfaces from all documents within this package.
    ///
    /// Each interface lists the document that it is from. Interfaces are listed
    /// in topological order as well so iteration through this arena will only
    /// reference prior elements already visited when working with recursive
    /// references.
    pub interfaces: Arena<Interface>,

    /// All types from all documents within this package.
    ///
    /// Each type lists the interface or world that defined it, or nothing if
    /// it's an anonymous type. Types are listed in this arena in topological
    /// order to ensure that iteration through this arena will only reference
    /// other types transitively that are already iterated over.
    pub types: Arena<TypeDef>,

    /// All foreign dependencies that this package depends on.
    ///
    /// These foreign dependencies must be resolved to convert this unresolved
    /// package into a `Resolve`. The map here is keyed by the name of the
    /// foreign package that this depends on, and the sub-map is keyed by an
    /// interface name followed by the identifier within `self.interfaces`. The
    /// fields of `self.interfaces` describes the required types that are from
    /// each foreign interface.
    pub foreign_deps: IndexMap<PackageName, IndexMap<String, AstItem>>,

    /// Doc comments for this package.
    pub docs: Docs,

    package_name_span: Span,
    unknown_type_spans: Vec<Span>,
    interface_spans: Vec<InterfaceSpan>,
    world_spans: Vec<WorldSpan>,
    type_spans: Vec<Span>,
    foreign_dep_spans: Vec<Span>,
    required_resource_types: Vec<(TypeId, Span)>,
}

/// Tracks a set of packages, all pulled from the same group of WIT source files.
#[derive(Clone)]
pub struct UnresolvedPackageGroup {
    /// The "main" package in this package group which was found at the root of
    /// the WIT files.
    ///
    /// Note that this is required to be present in all WIT files.
    pub main: UnresolvedPackage,

    /// Nested packages found while parsing `main`, if any.
    pub nested: Vec<UnresolvedPackage>,

    /// A set of processed source files from which these packages have been parsed.
    pub source_map: SourceMap,
}

#[derive(Clone)]
struct WorldSpan {
    span: Span,
    imports: Vec<Span>,
    exports: Vec<Span>,
    includes: Vec<Span>,
}

#[derive(Clone)]
struct InterfaceSpan {
    span: Span,
    funcs: Vec<Span>,
}

#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum AstItem {
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Interface(InterfaceId),
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    World(WorldId),
}

/// A structure used to keep track of the name of a package, containing optional
/// information such as a namespace and version information.
///
/// This is directly encoded as an "ID" in the binary component representation
/// with an interfaced tacked on as well.
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(into = "String"))]
pub struct PackageName {
    /// A namespace such as `wasi` in `wasi:foo/bar`
    pub namespace: String,
    /// The kebab-name of this package, which is always specified.
    pub name: String,
    /// Optional major/minor version information.
    pub version: Option<Version>,
}

impl From<PackageName> for String {
    fn from(name: PackageName) -> String {
        name.to_string()
    }
}

impl PackageName {
    /// Returns the ID that this package name would assign the `interface` name
    /// specified.
    pub fn interface_id(&self, interface: &str) -> String {
        let mut s = String::new();
        s.push_str(&format!("{}:{}/{interface}", self.namespace, self.name));
        if let Some(version) = &self.version {
            s.push_str(&format!("@{version}"));
        }
        s
    }

    /// Determines the "semver compatible track" for the given version.
    ///
    /// This method implements the logic from the component model where semver
    /// versions can be compatible with one another. For example versions 1.2.0
    /// and 1.2.1 would be considered both compatible with one another because
    /// they're on the same semver compatible track.
    ///
    /// This predicate is used during
    /// [`Resolve::merge_world_imports_based_on_semver`] for example to
    /// determine whether two imports can be merged together. This is
    /// additionally used when creating components to match up imports in
    /// core wasm to imports in worlds.
    pub fn version_compat_track(version: &Version) -> Version {
        let mut version = version.clone();
        version.build = semver::BuildMetadata::EMPTY;
        if !version.pre.is_empty() {
            return version;
        }
        if version.major != 0 {
            version.minor = 0;
            version.patch = 0;
            return version;
        }
        if version.minor != 0 {
            version.patch = 0;
            return version;
        }
        version
    }

    /// Returns the string corresponding to
    /// [`PackageName::version_compat_track`]. This is done to match the
    /// component model's expected naming scheme of imports and exports.
    pub fn version_compat_track_string(version: &Version) -> String {
        let version = Self::version_compat_track(version);
        if !version.pre.is_empty() {
            return version.to_string();
        }
        if version.major != 0 {
            return format!("{}", version.major);
        }
        if version.minor != 0 {
            return format!("{}.{}", version.major, version.minor);
        }
        version.to_string()
    }
}

impl fmt::Display for PackageName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.namespace, self.name)?;
        if let Some(version) = &self.version {
            write!(f, "@{version}")?;
        }
        Ok(())
    }
}

#[derive(Debug)]
struct Error {
    span: Span,
    msg: String,
    highlighted: Option<String>,
}

impl Error {
    fn new(span: Span, msg: impl Into<String>) -> Error {
        Error {
            span,
            msg: msg.into(),
            highlighted: None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.highlighted.as_ref().unwrap_or(&self.msg).fmt(f)
    }
}

impl std::error::Error for Error {}

#[derive(Debug)]
struct PackageNotFoundError {
    span: Span,
    requested: PackageName,
    known: Vec<PackageName>,
    highlighted: Option<String>,
}

impl PackageNotFoundError {
    pub fn new(span: Span, requested: PackageName, known: Vec<PackageName>) -> Self {
        Self {
            span,
            requested,
            known,
            highlighted: None,
        }
    }
}

impl fmt::Display for PackageNotFoundError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(highlighted) = &self.highlighted {
            return highlighted.fmt(f);
        }
        if self.known.is_empty() {
            write!(
                f,
                "package '{}' not found. no known packages.",
                self.requested
            )?;
        } else {
            write!(
                f,
                "package '{}' not found. known packages:\n",
                self.requested
            )?;
            for known in self.known.iter() {
                write!(f, "    {known}\n")?;
            }
        }
        Ok(())
    }
}

impl std::error::Error for PackageNotFoundError {}

impl UnresolvedPackageGroup {
    /// Parses the given string as a wit document.
    ///
    /// The `path` argument is used for error reporting. The `contents` provided
    /// are considered to be the contents of `path`. This function does not read
    /// the filesystem.
    pub fn parse(path: impl AsRef<Path>, contents: &str) -> Result<UnresolvedPackageGroup> {
        let mut map = SourceMap::default();
        map.push(path.as_ref(), contents);
        map.parse()
    }

    /// Parse a WIT package at the provided path.
    ///
    /// The path provided is inferred whether it's a file or a directory. A file
    /// is parsed with [`UnresolvedPackageGroup::parse_file`] and a directory is
    /// parsed with [`UnresolvedPackageGroup::parse_dir`].
    pub fn parse_path(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> {
        let path = path.as_ref();
        if path.is_dir() {
            UnresolvedPackageGroup::parse_dir(path)
        } else {
            UnresolvedPackageGroup::parse_file(path)
        }
    }

    /// Parses a WIT package from the file provided.
    ///
    /// The return value represents all packages found in the WIT file which
    /// might be either one or multiple depending on the syntax used.
    pub fn parse_file(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> {
        let path = path.as_ref();
        let contents = std::fs::read_to_string(path)
            .with_context(|| format!("failed to read file {path:?}"))?;
        Self::parse(path, &contents)
    }

    /// Parses a WIT package from the directory provided.
    ///
    /// This method will look at all files under the `path` specified. All
    /// `*.wit` files are parsed and assumed to be part of the same package
    /// grouping. This is useful when a WIT package is split across multiple
    /// files.
    pub fn parse_dir(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> {
        let path = path.as_ref();
        let mut map = SourceMap::default();
        let cx = || format!("failed to read directory {path:?}");
        for entry in path.read_dir().with_context(&cx)? {
            let entry = entry.with_context(&cx)?;
            let path = entry.path();
            let ty = entry.file_type().with_context(&cx)?;
            if ty.is_dir() {
                continue;
            }
            if ty.is_symlink() {
                if path.is_dir() {
                    continue;
                }
            }
            let filename = match path.file_name().and_then(|s| s.to_str()) {
                Some(name) => name,
                None => continue,
            };
            if !filename.ends_with(".wit") {
                continue;
            }
            map.push_file(&path)?;
        }
        map.parse()
    }
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct World {
    /// The WIT identifier name of this world.
    pub name: String,

    /// All imported items into this interface, both worlds and functions.
    pub imports: IndexMap<WorldKey, WorldItem>,

    /// All exported items from this interface, both worlds and functions.
    pub exports: IndexMap<WorldKey, WorldItem>,

    /// The package that owns this world.
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))]
    pub package: Option<PackageId>,

    /// Documentation associated with this world declaration.
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,

    /// Stability annotation for this world itself.
    #[cfg_attr(
        feature = "serde",
        serde(skip_serializing_if = "Stability::is_unknown")
    )]
    pub stability: Stability,

    /// All the included worlds from this world. Empty if this is fully resolved
    #[cfg_attr(feature = "serde", serde(skip))]
    pub includes: Vec<(Stability, WorldId)>,

    /// All the included worlds names. Empty if this is fully resolved
    #[cfg_attr(feature = "serde", serde(skip))]
    pub include_names: Vec<Vec<IncludeName>>,
}

#[derive(Debug, Clone)]
pub struct IncludeName {
    /// The name of the item
    pub name: String,

    /// The name to be replaced with
    pub as_: String,
}

/// The key to the import/export maps of a world. Either a kebab-name or a
/// unique interface.
#[derive(Debug, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(into = "String"))]
pub enum WorldKey {
    /// A kebab-name.
    Name(String),
    /// An interface which is assigned no kebab-name.
    Interface(InterfaceId),
}

impl Hash for WorldKey {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        match self {
            WorldKey::Name(s) => {
                0u8.hash(hasher);
                s.as_str().hash(hasher);
            }
            WorldKey::Interface(i) => {
                1u8.hash(hasher);
                i.hash(hasher);
            }
        }
    }
}

impl PartialEq for WorldKey {
    fn eq(&self, other: &WorldKey) -> bool {
        match (self, other) {
            (WorldKey::Name(a), WorldKey::Name(b)) => a.as_str() == b.as_str(),
            (WorldKey::Name(_), _) => false,
            (WorldKey::Interface(a), WorldKey::Interface(b)) => a == b,
            (WorldKey::Interface(_), _) => false,
        }
    }
}

impl From<WorldKey> for String {
    fn from(key: WorldKey) -> String {
        match key {
            WorldKey::Name(name) => name,
            WorldKey::Interface(id) => format!("interface-{}", id.index()),
        }
    }
}

impl WorldKey {
    /// Asserts that this is `WorldKey::Name` and returns the name.
    #[track_caller]
    pub fn unwrap_name(self) -> String {
        match self {
            WorldKey::Name(name) => name,
            WorldKey::Interface(_) => panic!("expected a name, found interface"),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum WorldItem {
    /// An interface is being imported or exported from a world, indicating that
    /// it's a namespace of functions.
    Interface {
        #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
        id: InterfaceId,
        #[cfg_attr(
            feature = "serde",
            serde(skip_serializing_if = "Stability::is_unknown")
        )]
        stability: Stability,
    },

    /// A function is being directly imported or exported from this world.
    Function(Function),

    /// A type is being exported from this world.
    ///
    /// Note that types are never imported into worlds at this time.
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Type(TypeId),
}

impl WorldItem {
    pub fn stability<'a>(&'a self, resolve: &'a Resolve) -> &'a Stability {
        match self {
            WorldItem::Interface { stability, .. } => stability,
            WorldItem::Function(f) => &f.stability,
            WorldItem::Type(id) => &resolve.types[*id].stability,
        }
    }
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Interface {
    /// Optionally listed name of this interface.
    ///
    /// This is `None` for inline interfaces in worlds.
    pub name: Option<String>,

    /// Exported types from this interface.
    ///
    /// Export names are listed within the types themselves. Note that the
    /// export name here matches the name listed in the `TypeDef`.
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))]
    pub types: IndexMap<String, TypeId>,

    /// Exported functions from this interface.
    pub functions: IndexMap<String, Function>,

    /// Documentation associated with this interface.
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,

    /// Stability attribute for this interface.
    #[cfg_attr(
        feature = "serde",
        serde(skip_serializing_if = "Stability::is_unknown")
    )]
    pub stability: Stability,

    /// The package that owns this interface.
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))]
    pub package: Option<PackageId>,
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct TypeDef {
    pub name: Option<String>,
    pub kind: TypeDefKind,
    pub owner: TypeOwner,
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,
    /// Stability attribute for this type.
    #[cfg_attr(
        feature = "serde",
        serde(skip_serializing_if = "Stability::is_unknown")
    )]
    pub stability: Stability,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum TypeDefKind {
    Record(Record),
    Resource,
    Handle(Handle),
    Flags(Flags),
    Tuple(Tuple),
    Variant(Variant),
    Enum(Enum),
    Option(Type),
    Result(Result_),
    List(Type),
    FixedSizeList(Type, u32),
    Future(Option<Type>),
    Stream(Option<Type>),
    Type(Type),

    /// This represents a type of unknown structure imported from a foreign
    /// interface.
    ///
    /// This variant is only used during the creation of `UnresolvedPackage` but
    /// by the time a `Resolve` is created then this will not exist.
    Unknown,
}

impl TypeDefKind {
    pub fn as_str(&self) -> &'static str {
        match self {
            TypeDefKind::Record(_) => "record",
            TypeDefKind::Resource => "resource",
            TypeDefKind::Handle(handle) => match handle {
                Handle::Own(_) => "own",
                Handle::Borrow(_) => "borrow",
            },
            TypeDefKind::Flags(_) => "flags",
            TypeDefKind::Tuple(_) => "tuple",
            TypeDefKind::Variant(_) => "variant",
            TypeDefKind::Enum(_) => "enum",
            TypeDefKind::Option(_) => "option",
            TypeDefKind::Result(_) => "result",
            TypeDefKind::List(_) => "list",
            TypeDefKind::FixedSizeList(..) => "fixed size list",
            TypeDefKind::Future(_) => "future",
            TypeDefKind::Stream(_) => "stream",
            TypeDefKind::Type(_) => "type",
            TypeDefKind::Unknown => "unknown",
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum TypeOwner {
    /// This type was defined within a `world` block.
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    World(WorldId),
    /// This type was defined within an `interface` block.
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Interface(InterfaceId),
    /// This type wasn't inherently defined anywhere, such as a `list<T>`, which
    /// doesn't need an owner.
    #[cfg_attr(feature = "serde", serde(untagged, serialize_with = "serialize_none"))]
    None,
}

#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum Handle {
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Own(TypeId),
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Borrow(TypeId),
}

#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum Type {
    Bool,
    U8,
    U16,
    U32,
    U64,
    S8,
    S16,
    S32,
    S64,
    F32,
    F64,
    Char,
    String,
    ErrorContext,
    Id(TypeId),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Int {
    U8,
    U16,
    U32,
    U64,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Record {
    pub fields: Vec<Field>,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Field {
    pub name: String,
    #[cfg_attr(feature = "serde", serde(rename = "type"))]
    pub ty: Type,
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Flags {
    pub flags: Vec<Flag>,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Flag {
    pub name: String,
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,
}

#[derive(Debug, Clone, PartialEq)]
pub enum FlagsRepr {
    U8,
    U16,
    U32(usize),
}

impl Flags {
    pub fn repr(&self) -> FlagsRepr {
        match self.flags.len() {
            0 => FlagsRepr::U32(0),
            n if n <= 8 => FlagsRepr::U8,
            n if n <= 16 => FlagsRepr::U16,
            n => FlagsRepr::U32(sizealign::align_to(n, 32) / 32),
        }
    }
}

impl FlagsRepr {
    pub fn count(&self) -> usize {
        match self {
            FlagsRepr::U8 => 1,
            FlagsRepr::U16 => 1,
            FlagsRepr::U32(n) => *n,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Tuple {
    pub types: Vec<Type>,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Variant {
    pub cases: Vec<Case>,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Case {
    pub name: String,
    #[cfg_attr(feature = "serde", serde(rename = "type"))]
    pub ty: Option<Type>,
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,
}

impl Variant {
    pub fn tag(&self) -> Int {
        discriminant_type(self.cases.len())
    }
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Enum {
    pub cases: Vec<EnumCase>,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct EnumCase {
    pub name: String,
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,
}

impl Enum {
    pub fn tag(&self) -> Int {
        discriminant_type(self.cases.len())
    }
}

/// This corresponds to the `discriminant_type` function in the Canonical ABI.
fn discriminant_type(num_cases: usize) -> Int {
    match num_cases.checked_sub(1) {
        None => Int::U8,
        Some(n) if n <= u8::max_value() as usize => Int::U8,
        Some(n) if n <= u16::max_value() as usize => Int::U16,
        Some(n) if n <= u32::max_value() as usize => Int::U32,
        _ => panic!("too many cases to fit in a repr"),
    }
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Result_ {
    pub ok: Option<Type>,
    pub err: Option<Type>,
}

#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Docs {
    pub contents: Option<String>,
}

impl Docs {
    pub fn is_empty(&self) -> bool {
        self.contents.is_none()
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Function {
    pub name: String,
    pub kind: FunctionKind,
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_params"))]
    pub params: Vec<(String, Type)>,
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
    pub result: Option<Type>,
    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
    pub docs: Docs,
    /// Stability attribute for this function.
    #[cfg_attr(
        feature = "serde",
        serde(skip_serializing_if = "Stability::is_unknown")
    )]
    pub stability: Stability,
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum FunctionKind {
    /// A freestanding function.
    ///
    /// ```wit
    /// interface foo {
    ///     the-func: func();
    /// }
    /// ```
    Freestanding,

    /// An async freestanding function.
    ///
    /// ```wit
    /// interface foo {
    ///     the-func: async func();
    /// }
    /// ```
    AsyncFreestanding,

    /// A resource method where the first parameter is implicitly
    /// `borrow<T>`.
    ///
    /// ```wit
    /// interface foo {
    ///     resource r {
    ///         the-func: func();
    ///     }
    /// }
    /// ```
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Method(TypeId),

    /// An async resource method where the first parameter is implicitly
    /// `borrow<T>`.
    ///
    /// ```wit
    /// interface foo {
    ///     resource r {
    ///         the-func: async func();
    ///     }
    /// }
    /// ```
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    AsyncMethod(TypeId),

    /// A static resource method.
    ///
    /// ```wit
    /// interface foo {
    ///     resource r {
    ///         the-func: static func();
    ///     }
    /// }
    /// ```
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Static(TypeId),

    /// An async static resource method.
    ///
    /// ```wit
    /// interface foo {
    ///     resource r {
    ///         the-func: static async func();
    ///     }
    /// }
    /// ```
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    AsyncStatic(TypeId),

    /// A resource constructor where the return value is implicitly `own<T>`.
    ///
    /// ```wit
    /// interface foo {
    ///     resource r {
    ///         constructor();
    ///     }
    /// }
    /// ```
    #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
    Constructor(TypeId),
}

impl FunctionKind {
    /// Returns whether this is an async function or not.
    pub fn is_async(&self) -> bool {
        match self {
            FunctionKind::Freestanding
            | FunctionKind::Method(_)
            | FunctionKind::Static(_)
            | FunctionKind::Constructor(_) => false,
            FunctionKind::AsyncFreestanding
            | FunctionKind::AsyncMethod(_)
            | FunctionKind::AsyncStatic(_) => true,
        }
    }

    /// Returns the resource, if present, that this function kind refers to.
    pub fn resource(&self) -> Option<TypeId> {
        match self {
            FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None,
            FunctionKind::Method(id)
            | FunctionKind::Static(id)
            | FunctionKind::Constructor(id)
            | FunctionKind::AsyncMethod(id)
            | FunctionKind::AsyncStatic(id) => Some(*id),
        }
    }

    /// Returns the resource, if present, that this function kind refers to.
    pub fn resource_mut(&mut self) -> Option<&mut TypeId> {
        match self {
            FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None,
            FunctionKind::Method(id)
            | FunctionKind::Static(id)
            | FunctionKind::Constructor(id)
            | FunctionKind::AsyncMethod(id)
            | FunctionKind::AsyncStatic(id) => Some(id),
        }
    }
}

/// Possible forms of name mangling that are supported by this crate.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Mangling {
    /// The "standard" component model mangling format for 32-bit linear
    /// memories. This is specified in WebAssembly/component-model#378
    Standard32,

    /// The "legacy" name mangling supported in versions 218-and-prior for this
    /// crate. This is the original support for how components were created from
    /// core wasm modules and this does not correspond to any standard. This is
    /// preserved for now while tools transition to the new scheme.
    Legacy,
}

impl std::str::FromStr for Mangling {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Mangling> {
        match s {
            "legacy" => Ok(Mangling::Legacy),
            "standard32" => Ok(Mangling::Standard32),
            _ => {
                bail!(
                    "unknown name mangling `{s}`, \
                     supported values are `legacy` or `standard32`"
                )
            }
        }
    }
}

/// Possible lift/lower ABI choices supported when mangling names.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LiftLowerAbi {
    /// Both imports and exports will use the synchronous ABI.
    Sync,

    /// Both imports and exports will use the async ABI (with a callback for
    /// each export).
    AsyncCallback,

    /// Both imports and exports will use the async ABI (with no callbacks for
    /// exports).
    AsyncStackful,
}

impl LiftLowerAbi {
    fn import_prefix(self) -> &'static str {
        match self {
            Self::Sync => "",
            Self::AsyncCallback | Self::AsyncStackful => "[async-lower]",
        }
    }

    /// Get the import [`AbiVariant`] corresponding to this [`LiftLowerAbi`]
    pub fn import_variant(self) -> AbiVariant {
        match self {
            Self::Sync => AbiVariant::GuestImport,
            Self::AsyncCallback | Self::AsyncStackful => AbiVariant::GuestImportAsync,
        }
    }

    fn export_prefix(self) -> &'static str {
        match self {
            Self::Sync => "",
            Self::AsyncCallback => "[async-lift]",
            Self::AsyncStackful => "[async-lift-stackful]",
        }
    }

    /// Get the export [`AbiVariant`] corresponding to this [`LiftLowerAbi`]
    pub fn export_variant(self) -> AbiVariant {
        match self {
            Self::Sync => AbiVariant::GuestExport,
            Self::AsyncCallback => AbiVariant::GuestExportAsync,
            Self::AsyncStackful => AbiVariant::GuestExportAsyncStackful,
        }
    }
}

/// Combination of [`Mangling`] and [`LiftLowerAbi`].
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ManglingAndAbi {
    /// See [`Mangling::Standard32`].
    ///
    /// As of this writing, the standard name mangling only supports the
    /// synchronous ABI.
    Standard32,

    /// See [`Mangling::Legacy`] and [`LiftLowerAbi`].
    Legacy(LiftLowerAbi),
}

impl ManglingAndAbi {
    /// Get the import [`AbiVariant`] corresponding to this [`ManglingAndAbi`]
    pub fn import_variant(self) -> AbiVariant {
        match self {
            Self::Standard32 => AbiVariant::GuestImport,
            Self::Legacy(abi) => abi.import_variant(),
        }
    }

    /// Get the export [`AbiVariant`] corresponding to this [`ManglingAndAbi`]
    pub fn export_variant(self) -> AbiVariant {
        match self {
            Self::Standard32 => AbiVariant::GuestExport,
            Self::Legacy(abi) => abi.export_variant(),
        }
    }

    /// Switch the ABI to be sync if it's async.
    pub fn sync(self) -> Self {
        match self {
            Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => self,
            Self::Legacy(LiftLowerAbi::AsyncCallback)
            | Self::Legacy(LiftLowerAbi::AsyncStackful) => Self::Legacy(LiftLowerAbi::Sync),
        }
    }

    /// Returns whether this is an async ABI
    pub fn is_async(&self) -> bool {
        match self {
            Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => false,
            Self::Legacy(LiftLowerAbi::AsyncCallback)
            | Self::Legacy(LiftLowerAbi::AsyncStackful) => true,
        }
    }

    pub fn mangling(&self) -> Mangling {
        match self {
            Self::Standard32 => Mangling::Standard32,
            Self::Legacy(_) => Mangling::Legacy,
        }
    }
}

impl Function {
    pub fn item_name(&self) -> &str {
        match &self.kind {
            FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &self.name,
            FunctionKind::Method(_)
            | FunctionKind::Static(_)
            | FunctionKind::AsyncMethod(_)
            | FunctionKind::AsyncStatic(_) => &self.name[self.name.find('.').unwrap() + 1..],
            FunctionKind::Constructor(_) => "constructor",
        }
    }

    /// Returns an iterator over the types used in parameters and results.
    ///
    /// Note that this iterator is not transitive, it only iterates over the
    /// direct references to types that this function has.
    pub fn parameter_and_result_types(&self) -> impl Iterator<Item = Type> + '_ {
        self.params.iter().map(|(_, t)| *t).chain(self.result)
    }

    /// Gets the core export name for this function.
    pub fn standard32_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> {
        self.core_export_name(interface, Mangling::Standard32)
    }

    pub fn legacy_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> {
        self.core_export_name(interface, Mangling::Legacy)
    }
    /// Gets the core export name for this function.
    pub fn core_export_name<'a>(
        &'a self,
        interface: Option<&str>,
        mangling: Mangling,
    ) -> Cow<'a, str> {
        match interface {
            Some(interface) => match mangling {
                Mangling::Standard32 => Cow::Owned(format!("cm32p2|{interface}|{}", self.name)),
                Mangling::Legacy => Cow::Owned(format!("{interface}#{}", self.name)),
            },
            None => match mangling {
                Mangling::Standard32 => Cow::Owned(format!("cm32p2||{}", self.name)),
                Mangling::Legacy => Cow::Borrowed(&self.name),
            },
        }
    }
    /// Collect any future and stream types appearing in the signature of this
    /// function by doing a depth-first search over the parameter types and then
    /// the result types.
    ///
    /// For example, given the WIT function `foo: func(x: future<future<u32>>,
    /// y: u32) -> stream<u8>`, we would return `[future<u32>,
    /// future<future<u32>>, stream<u8>]`.
    ///
    /// This may be used by binding generators to refer to specific `future` and
    /// `stream` types when importing canonical built-ins such as `stream.new`,
    /// `future.read`, etc.  Using the example above, the import
    /// `[future-new-0]foo` would indicate a call to `future.new` for the type
    /// `future<u32>`.  Likewise, `[future-new-1]foo` would indicate a call to
    /// `future.new` for `future<future<u32>>`, and `[stream-new-2]foo` would
    /// indicate a call to `stream.new` for `stream<u8>`.
    pub fn find_futures_and_streams(&self, resolve: &Resolve) -> Vec<TypeId> {
        let mut results = Vec::new();
        for (_, ty) in self.params.iter() {
            find_futures_and_streams(resolve, *ty, &mut results);
        }
        if let Some(ty) = self.result {
            find_futures_and_streams(resolve, ty, &mut results);
        }
        results
    }

    /// Check if this function is a resource constructor in shorthand form.
    /// I.e. without an explicit return type annotation.
    pub fn is_constructor_shorthand(&self, resolve: &Resolve) -> bool {
        let FunctionKind::Constructor(containing_resource_id) = self.kind else {
            return false;
        };

        let Some(Type::Id(id)) = &self.result else {
            return false;
        };

        let TypeDefKind::Handle(Handle::Own(returned_resource_id)) = resolve.types[*id].kind else {
            return false;
        };

        return containing_resource_id == returned_resource_id;
    }

    /// Returns the `module`, `name`, and signature to use when importing this
    /// function's `task.return` intrinsic using the `mangling` specified.
    pub fn task_return_import(
        &self,
        resolve: &Resolve,
        interface: Option<&WorldKey>,
        mangling: Mangling,
    ) -> (String, String, abi::WasmSignature) {
        match mangling {
            Mangling::Standard32 => todo!(),
            Mangling::Legacy => {}
        }
        // For exported async functions, generate a `task.return` intrinsic.
        let module = match interface {
            Some(key) => format!("[export]{}", resolve.name_world_key(key)),
            None => "[export]$root".to_string(),
        };
        let name = format!("[task-return]{}", self.name);

        let mut func_tmp = self.clone();
        func_tmp.params = Vec::new();
        func_tmp.result = None;
        if let Some(ty) = self.result {
            func_tmp.params.push(("x".to_string(), ty));
        }
        let sig = resolve.wasm_signature(AbiVariant::GuestImport, &func_tmp);
        (module, name, sig)
    }

    // push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func);
}

fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeId>) {
    let Type::Id(id) = ty else {
        return;
    };

    match &resolve.types[id].kind {
        TypeDefKind::Resource
        | TypeDefKind::Handle(_)
        | TypeDefKind::Flags(_)
        | TypeDefKind::Enum(_) => {}
        TypeDefKind::Record(r) => {
            for Field { ty, .. } in &r.fields {
                find_futures_and_streams(resolve, *ty, results);
            }
        }
        TypeDefKind::Tuple(t) => {
            for ty in &t.types {
                find_futures_and_streams(resolve, *ty, results);
            }
        }
        TypeDefKind::Variant(v) => {
            for Case { ty, .. } in &v.cases {
                if let Some(ty) = ty {
                    find_futures_and_streams(resolve, *ty, results);
                }
            }
        }
        TypeDefKind::Option(ty)
        | TypeDefKind::List(ty)
        | TypeDefKind::FixedSizeList(ty, ..)
        | TypeDefKind::Type(ty) => {
            find_futures_and_streams(resolve, *ty, results);
        }
        TypeDefKind::Result(r) => {
            if let Some(ty) = r.ok {
                find_futures_and_streams(resolve, ty, results);
            }
            if let Some(ty) = r.err {
                find_futures_and_streams(resolve, ty, results);
            }
        }
        TypeDefKind::Future(ty) => {
            if let Some(ty) = ty {
                find_futures_and_streams(resolve, *ty, results);
            }
            results.push(id);
        }
        TypeDefKind::Stream(ty) => {
            if let Some(ty) = ty {
                find_futures_and_streams(resolve, *ty, results);
            }
            results.push(id);
        }
        TypeDefKind::Unknown => unreachable!(),
    }
}

/// Representation of the stability attributes associated with a world,
/// interface, function, or type.
///
/// This is added for WebAssembly/component-model#332 where @since and @unstable
/// annotations were added to WIT.
///
/// The order of the of enum values is significant since it is used with Ord and PartialOrd
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde_derive::Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum Stability {
    /// This item does not have either `@since` or `@unstable`.
    Unknown,

    /// `@unstable(feature = foo)`
    ///
    /// This item is explicitly tagged `@unstable`. A feature name is listed and
    /// this item is excluded by default in `Resolve` unless explicitly enabled.
    Unstable {
        feature: String,
        #[cfg_attr(
            feature = "serde",
            serde(
                skip_serializing_if = "Option::is_none",
                default,
                serialize_with = "serialize_optional_version",
                deserialize_with = "deserialize_optional_version"
            )
        )]
        deprecated: Option<Version>,
    },

    /// `@since(version = 1.2.3)`
    ///
    /// This item is explicitly tagged with `@since` as stable since the
    /// specified version.  This may optionally have a feature listed as well.
    Stable {
        #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_version"))]
        #[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_version"))]
        since: Version,
        #[cfg_attr(
            feature = "serde",
            serde(
                skip_serializing_if = "Option::is_none",
                default,
                serialize_with = "serialize_optional_version",
                deserialize_with = "deserialize_optional_version"
            )
        )]
        deprecated: Option<Version>,
    },
}

impl Stability {
    /// Returns whether this is `Stability::Unknown`.
    pub fn is_unknown(&self) -> bool {
        matches!(self, Stability::Unknown)
    }

    pub fn is_stable(&self) -> bool {
        matches!(self, Stability::Stable { .. })
    }
}

impl Default for Stability {
    fn default() -> Stability {
        Stability::Unknown
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_discriminant_type() {
        assert_eq!(discriminant_type(1), Int::U8);
        assert_eq!(discriminant_type(0x100), Int::U8);
        assert_eq!(discriminant_type(0x101), Int::U16);
        assert_eq!(discriminant_type(0x10000), Int::U16);
        assert_eq!(discriminant_type(0x10001), Int::U32);
        if let Ok(num_cases) = usize::try_from(0x100000000_u64) {
            assert_eq!(discriminant_type(num_cases), Int::U32);
        }
    }

    #[test]
    fn test_find_futures_and_streams() {
        let mut resolve = Resolve::default();
        let t0 = resolve.types.alloc(TypeDef {
            name: None,
            kind: TypeDefKind::Future(Some(Type::U32)),
            owner: TypeOwner::None,
            docs: Docs::default(),
            stability: Stability::Unknown,
        });
        let t1 = resolve.types.alloc(TypeDef {
            name: None,
            kind: TypeDefKind::Future(Some(Type::Id(t0))),
            owner: TypeOwner::None,
            docs: Docs::default(),
            stability: Stability::Unknown,
        });
        let t2 = resolve.types.alloc(TypeDef {
            name: None,
            kind: TypeDefKind::Stream(Some(Type::U32)),
            owner: TypeOwner::None,
            docs: Docs::default(),
            stability: Stability::Unknown,
        });
        let found = Function {
            name: "foo".into(),
            kind: FunctionKind::Freestanding,
            params: vec![("p1".into(), Type::Id(t1)), ("p2".into(), Type::U32)],
            result: Some(Type::Id(t2)),
            docs: Docs::default(),
            stability: Stability::Unknown,
        }
        .find_futures_and_streams(&resolve);
        assert_eq!(3, found.len());
        assert_eq!(t0, found[0]);
        assert_eq!(t1, found[1]);
        assert_eq!(t2, found[2]);
    }
}
